Skip to content

feat(linux): ship a Flatpak bundle with each release - #767

Open
AalmanSadath wants to merge 4 commits into
AprilNEA:masterfrom
AalmanSadath:feat/linux-flatpak
Open

feat(linux): ship a Flatpak bundle with each release#767
AalmanSadath wants to merge 4 commits into
AprilNEA:masterfrom
AalmanSadath:feat/linux-flatpak

Conversation

@AalmanSadath

@AalmanSadath AalmanSadath commented Aug 21, 2026

Copy link
Copy Markdown

Summary

Adds a .flatpak bundle per architecture to the release assets, beside the
.deb/.rpm/.pkg.tar.zst already there. One new job in the existing build
matrix, and a few edits to release.yml to sign and attach what it produces.

This PR has been reworked and no longer publishes a repository. It opened
as a signed OSTree repository on Pages, which meant a workflow to run, a GPG
key to hold, and hosting to keep alive. Since then I ran the numbers on what a
Flatpak userbase would actually pull through such a remote, and Pages does not
hold up (see the earlier comment in this thread). Rather than ask you to adopt
R2 and a signing key on my say-so, this is now the part that needs none of it.

What that costs: a bundle is a one-time install. There is no remote behind it,
so flatpak update has nothing to check. That is a real limitation and it is
stated plainly in the docs. It also does not close #605. What it does give you
is an install path for atomic distributions (#681), where layering an rpm is
the current answer, at the price of one job in a matrix.

Adding a repository later is still possible and nothing here works against it —
the manifest, metadata and build steps are the same either way. That decision
just stops being a condition of merging this.

No new workflow file, no OSTree repo, no signing key, no hosting.

Changes

packaging/linux/flatpak/ (new)

  • org.openlogi.OpenLogi.yml, the manifest. Source is type: dir over the
    checkout, so a release build is exactly the tagged tree.
  • .desktop and AppStream metadata. Flatpak requires both to be named for the
    application ID, which the packaged .desktop is not, and there is no
    AppStream data in the tree, which software centres need to list the app.

Only the build environment lives in the manifest, because that part is
genuinely packaging's problem:

  • rustup rather than org.freedesktop.Sdk.Extension.rust-stable. The workspace
    sets rust-version to current stable and the extension trails it by weeks
    (1.97.1 against a workspace asking for 1.98), which cargo refuses outright.
    rustup installs what rust-toolchain.toml names.
  • llvm20, because openlogi-camera pulls v4l2-sys-mit, whose build script
    runs bindgen and dlopens libclang. The base SDK ships none.

Permissions worth a look: --device=all is the only one covering both
/dev/hidraw* and /dev/uinput--device=input covers neither. It grants a
path to those nodes, not DAC permission; the host still needs
70-openlogi.rules, exactly as the .deb does. Solaar carries the same
permission on Flathub for the same reason.

.github/workflows/build.yml

New linux-flatpak job, matrix amd64/arm64 on the same runners as
linux-packages, wired into results and the workflow_call outputs.

Separate from linux-packages on purpose: it compiles the workspace again
inside the Flatpak SDK, where the runner's rust-cache cannot reach, so folding
them together would lose the .deb whenever the Flatpak build failed.

AppStream is stamped from the tree — version from the workspace, date from the
commit — never from an input, so a rebuild of an old tag cannot claim to have
been released the day it was rebuilt.

.github/workflows/release.yml

Downloads the artifacts, adds *.flatpak to SHA256SUMS and to the minisign
loop, and attaches the bundles best-effort. It rides the existing degradation
path: publish gates only on macos_result, so a failed Flatpak leg costs the
bundle, not the release.

DocsREADME.md and docs/INSTALL-linux.md cover the install and the
one-time host udev setup, which is a single command. .claude/rules/ci.md
records the new leg and its label gate.

Testing

Dispatched Release on the branch (publish skipped, ref_type != 'tag'):

  • linux-flatpak (amd64) — pass
  • linux-flatpak (arm64) — pass
  • macOS/Windows legs fail in a fork for want of signing secrets, which is
    expected and unrelated

Locally, from the repository root:

flatpak-builder --user --disable-rofiles-fuse --force-clean \
  --repo=repo --default-branch=stable \
  build packaging/linux/flatpak/org.openlogi.OpenLogi.yml
flatpak build-bundle --runtime-repo=https://dl.flathub.org/repo/flathub.flatpakrepo \
  repo out.flatpak org.openlogi.OpenLogi stable
appstreamcli validate packaging/linux/flatpak/org.openlogi.OpenLogi.metainfo.xml
desktop-file-validate packaging/linux/flatpak/org.openlogi.OpenLogi.desktop

Both exit 0 in ~10 minutes; metainfo and desktop validate. Re-imported the
bundle and confirmed the contents: all four binaries, the stamped version,
70-openlogi.rules, every icon size, and the runtime repo embedded.

Installed the amd64 CI artifact on Fedora 44 and confirmed it runs.
Not runtime-tested against hardware beyond that — device access depends on
the host udev rules, which that machine already had.

The release.yml half is not exercised by any of the above, because publish
needs a tag and a successful macOS leg, which a fork cannot produce. I ran its
shell against a fabricated dist/ instead: with bundles present both land in
SHA256SUMS, get signed, and are attached; with the leg failed, checksums still
generate and fail_on_unmatched_files does not trip.

Notes for review

  • CI will not build this on the PR. should-build returns false for
    pull_request without the needs: build label, so a green check here has not
    run the new job.
  • publish has needs: build, so a release now waits for the Flatpak legs even
    though it does not depend on them. Measured ~15 minutes cold on
    ubuntu-latest; timeout-minutes: 45 is a hang bound, not an estimate.

Refs #605, #681.

Fix #371

@AalmanSadath
AalmanSadath requested a review from AprilNEA as a code owner August 21, 2026 18:41
@davidbudnick davidbudnick added type: feature New feature request platform: linux Linux-specific issue labels Aug 21, 2026
@greptile-apps

greptile-apps Bot commented Aug 21, 2026

Copy link
Copy Markdown

Greptile Summary

The PR adds amd64 and arm64 Flatpak bundles to the existing Linux packaging and release pipeline.

  • Builds the complete OpenLogi product set inside the Freedesktop SDK.
  • Stamps AppStream metadata from the checked-out workspace and commit.
  • Adds generated bundles to release checksums, signatures, R2 storage, and GitHub Release assets.
  • Documents installation and the required host-side udev setup.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
.github/workflows/build.yml Adds architecture-verified Flatpak builds, workspace-derived AppStream stamping, bundle creation, and reusable-workflow result reporting.
.github/workflows/release.yml Incorporates available Flatpak bundles into the existing best-effort Linux release, checksum, signing, R2, and GitHub publication paths.
packaging/linux/flatpak/org.openlogi.OpenLogi.yml Defines the Flatpak runtime, sandbox permissions, build environment, complete binary set, desktop integration, icons, udev rules, and licenses.
packaging/linux/flatpak/org.openlogi.OpenLogi.metainfo.xml Adds AppStream metadata whose release version and date are replaced during CI from the checked-out source tree.
docs/INSTALL-linux.md Documents bundle installation, the lack of an update remote, and the mandatory host-side udev setup.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  S[Checked-out release tree] --> B[Flatpak matrix build]
  B --> X[amd64 bundle]
  B --> A[arm64 bundle]
  X --> P[Release publisher]
  A --> P
  P --> C[SHA-256 checksums]
  P --> M[Minisign signatures]
  C --> G[GitHub Release and R2]
  M --> G
Loading

Reviews (5): Last reviewed commit: "docs(linux): document the Flatpak bundle..." | Re-trigger Greptile

Comment thread .github/workflows/flatpak.yml Outdated
Comment thread .github/workflows/flatpak.yml Outdated
@AalmanSadath

AalmanSadath commented Aug 21, 2026

Copy link
Copy Markdown
Author

Both review findings are resolved.

Version/ref mismatch (P1). Fixed in 754a2e5. resolve now emits the ref alongside the version and the two move together: a release builds its tag, a dispatch naming a version builds that tag, and a dispatch naming none builds the ref it ran on and labels it with the workspace version. Previously the version was a label only, so a dispatch could have published one tree signed under another tree's release identity.

Action pinning (P2). Settled as a non-issue for this workflow: every action in the publish job is first-party actions/*, on the same mutable tags release.yml and windows-sign-dryrun.yml already use while handling Apple codesign certs and Azure signing credentials. Pinning here alone would make it the only pinned workflow in the tree, and with no Dependabot or Renovate config the SHAs would go stale unnoticed. 12ec9e1 records that reasoning next to the job so it does not have to be rediscovered, and says to follow a repo-wide pinning policy here if one is ever adopted. Happy to pin if you would prefer it, or to do the repo-wide version as a separate PR.

CI status. Green on 12ec9e1, including the macOS test matrix. Locally all three commits also pass cargo xtask ci: 8 passed, 0 failed, 1 skipped, the skip being tests (macos) for want of a Mac.

@davidbudnick davidbudnick added this to the v0.7.5 milestone Aug 21, 2026
@AalmanSadath

Copy link
Copy Markdown
Author

Thanks for the approval. One thing to flag before v0.7.5 ships: publishing is gated on a FLATPAK_GPG_PRIVATE_KEY repository secret. Without it the workflow still builds both architectures and uploads artifacts, but the publish job skips and no repository is deployed, so the remote that users would add based on the docs wouldn't exist yet.

Whoever sets the key will also need to set GitHub Pages to deploy from GitHub Actions. Happy to write up the key generation steps if that's useful.

@davidbudnick

Copy link
Copy Markdown
Collaborator

FLATPAK_GPG_PRIVATE_KEY

Great call out @AprilNEA would have the update the ENV variable before going live :)

@AalmanSadath

Copy link
Copy Markdown
Author

Just to clarify where it goes, it's a repository secret rather than an env variable, under
Settings -> Secrets and variables -> Actions,
since the workflow reads it as secrets.FLATPAK_GPG_PRIVATE_KEY.

@AalmanSadath

AalmanSadath commented Aug 23, 2026

Copy link
Copy Markdown
Author

I ran the numbers on what a Flatpak userbase would put through this remote, which I should have done before opening the PR, and Pages doesn't hold up as I didn't take into account how many users use this app.

Upstream releases about 17 times a month, and a full pull is roughly 31.5 MiB per arch. At a few thousand people updating, that's somewhere between 200 GB and 1 TB a month against a 100 GB allowance on GitHub Pages. Deltas help but they can't close a 5x gap, since at this release cadence people are often more than one commit behind and the delta stops applying.

I'd also like to have a go at getting this onto Flathub. It needs vendored offline builds. I've scoped that out and it's doable, but realistically a few weeks of verification to get approved.

So for the meantime, two options, rather than me rewriting this PR unasked:

  1. Drop the repo from this PR and just ship a bundle: flatpak build-bundle gives you one 21.2 MB file per arch as a release asset. Downside is no auto-updates, but Flathub can replace it in the future. The PR shrinks to a manifest plus one build job. It's still worth having on its own for distros where neither .deb nor .rpm is native, and for the immutable ones where Flatpak is the normal way to install anything.

  2. Switch it to Cloudflare R2: I've tested this end to end and signed commits verify fine. R2 doesn't charge for bandwidth or cap it, which is the thing that makes Pages unworkable here. Class B (GET or HEAD request) operations do scale with userbase: free up to about 9,600 clients (10M Class B ops), $0.15/mo at 10k, $3.90 at 20k, around $34 at 100k, worst case with no deltas applying.

    Those numbers are for the bucket on its own. Putting it behind a custom domain with a couple of cache rules changes that, since a cached request doesn't reach the bucket and so isn't billed as an operation. Origin traffic then works out at roughly 90 objects per release times however many datacentres serve them, which lands somewhere well under a million operations a month against a 10 million free tier. That holds at 10k users or 100k.

Whoever owns the bucket ends up owning the signing key and the bill, so that part is your call either way.


Since writing this I've put option 2 up so you can look at it working:
The flatpak repo: https://openlogi.aalman.dev
source: https://github.com/AalmanSadath/openlogi-flatpak

Same manifest as this PR with the publish switched to R2. Signed commits verify, the history goes back to v0.7.2 so deltas have something to apply against (about 3 MB for a one release jump instead of 31 MB), and with the cache rules in front of it everything sits inside the free tier. Each release there also carries the bundles from option 1, so both options are visible in one place.

This is a stopgap running on my own account, not me trying to own the Linux distribution. If you'd rather run it yourselves I'll hand the workflow over as is, and whenever that happens or Flathub lands, I'll archive mine and point people at yours.

@arteeh

arteeh commented Aug 24, 2026

Copy link
Copy Markdown

Please make it so it goes on Flathub. It will show up in everyone's app stores.

@DaAwesomeP

Copy link
Copy Markdown

Dropping the repo and starting with flatpak build-bundle would be a good first step. It adds a portable build (at least similar to what an AppImage would do) and would allow kicking off Flathub inclusion. Flathub is the de-facto place for Flatpaks; I think that a private repo is honestly a lot less attractive and a lot more to maintain.

davidbudnick
davidbudnick previously approved these changes Aug 25, 2026
@greptile-apps

greptile-apps Bot commented Aug 25, 2026

Copy link
Copy Markdown

Want your agent to iterate on Greptile's feedback? Start a greploop in Codex and it will work through the open comments and keep going until this PR reviews clean.

@AprilNEA AprilNEA modified the milestones: v0.8.0, v1.0.0 Aug 25, 2026
The .deb/.rpm/.pkg.tar.zst cover the distributions that use them, and leave
out the ones that use neither: the immutable distributions especially, where
Flatpak is simply how software is installed. This adds a single-file .flatpak
bundle per architecture to the release assets, beside the packages already
there.

A bundle is a one-time install rather than a subscription, since there is no
remote for `flatpak update` to check. That is the deliberate limit of this
change: closing it needs a repository to host and sign, which is a bigger
decision than adding a build artifact, and it can be taken later without
undoing any of this.

The build is its own leg rather than part of linux-packages. It compiles the
workspace again inside the Flatpak SDK, where the runner's rust-cache cannot
reach, so folding it in would tie a thirty-minute job to an hour-long one and
lose the .deb whenever the Flatpak build failed. It rides the same
best-effort path as the Windows and Linux package legs: a failure costs the
bundle, not the release.

AppStream is stamped from the tree being built, never from an input. The
version comes from the workspace and the date from the commit, so a rebuild
of an old tag cannot claim to have been released the day it was rebuilt, and
the label cannot drift from the binaries it names.

Verified locally end to end: flatpak-builder and build-bundle both exit 0, and
the resulting 23 MB bundle imports with all four binaries, the stamped
version, the udev rules, every icon size and the runtime repo embedded.
The Linux section listed the three package formats and nothing for the
distributions that use none of them, which is the gap the bundle exists to
fill.

Host setup is one command rather than the usual block. The rules are read out
of the installed application, so it needs no checkout and no cloned
repository, and nothing extra is needed for /dev/uinput: the rules already
create its node with static_node=uinput and opening it loads the module,
which is why no packaging here ships a modules-load.d entry either.

Also says what the bundle is not. There is no remote behind it, so
`flatpak update` has nothing to check, and the agent is launched by the
application rather than by a user service — so the systemctl line below it
does not apply.

Verified the one-liner against an installed bundle: it reproduces the tracked
rules file byte for byte.
The minisign loop covers the DMGs, the Windows zip/msi and all three Linux
package formats, and its own comment says why: manual verification today, and
the future auto-updaters need a detached signature to exist for every shipped
version. The bundle was left out of it.

That mattered in two places, not one. It is attached to the GitHub Release,
and the R2 step copies dist/ wholesale, so the bundle was already being
published to both without a .minisig beside it — the one installer a user
could not verify.

The signatures themselves need no further wiring: dist/*.minisig is already
listed unconditionally in the release assets and R2 excludes only latest.json
and the exes.

Reported by Greptile on AprilNEA#957.
Carries across the INSTALL-linux.md section from the repository-based
approach, rewritten for a bundle: no remote to add, so no auto-update, and the
udev step is the same one command rather than five.

Two of those five were never needed. The rules already create /dev/uinput at
boot with static_node=uinput and opening it loads the module, which is why no
packaging in this tree ships a modules-load.d entry either.

Also records the new build leg in the CI rules, including that the label gate
means a PR touching the manifest gets no feedback until someone applies it.
@AalmanSadath AalmanSadath changed the title feat(linux): package OpenLogi as a Flatpak with a signed repository feat(linux): ship a Flatpak bundle with each release Aug 25, 2026
@AalmanSadath

Copy link
Copy Markdown
Author

I've reworked this to drop the repository half. It now only builds the .flatpak
bundles and attaches them to the release, which is roughly half the size and
adds no new workflow.

The reason is the numbers I posted above: Pages does not carry the traffic a
Flatpak userbase would put through a remote, and the alternative is a bucket
plus a signing key. Both are yours to own and pay for, and I would rather not
make merging this conditional on that decision. A repository can still be added
later and nothing here works against it.

So the earlier FLATPAK_GPG_PRIVATE_KEY note no longer applies as there is
nothing to sign a repository with, and no secret to set. Sorry for the churn on
that, @davidbudnick.

What it does not do: bundles have no remote behind them, so flatpak update
has nothing to check and this does not close #605. It does give atomic
distributions an install path (#681).

Both arch legs build green in CI and the amd64 bundle installs and runs. One
thing worth knowing before judging the checks: should-build skips the whole
matrix for a PR without the needs: build label, so the green tick here has not
actually run the new job.

Also fixed a Greptile P1 from the closed duplicate: the minisign loop covers
every other shipped artifact, and the bundle was missing from it. It would have
been the one installer a user could not verify, in both the release and the R2
copy.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

platform: linux Linux-specific issue type: feature New feature request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Update failed: no release asset matched the current platform (linux/x86_64) [Feature]: Flatpak distribution / Publish on Flathub

5 participants